/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package weka.attributeSelection.semiAS;

import java.io.Serializable;
import weka.core.Capabilities;
import weka.core.CapabilitiesHandler;
import weka.core.Instances;
import weka.core.MySave;
import weka.core.OptionHandler;
import weka.core.RevisionHandler;
import weka.core.RevisionUtils;
import weka.core.Utils;
import weka.core.converters.ConverterUtils.DataSource;
import weka.filters.Filter;
import weka.filters.unsupervised.attribute.Remove;

/**
 *
 * @author Administrator
 */
abstract public class SemiAttributeSelector
        implements Serializable, CapabilitiesHandler, RevisionHandler, OptionHandler {

    protected Instances m_trainInstances;
    protected int m_classIndex;
    protected int m_numAttribs;
    protected int m_numInstances;
    protected boolean[] m_IsLabeled;//有监督/无监督标记数组
    protected double m_labeleWeight;//有监督距离权重值
    protected double m_unlabeleWeight;//有监督距离权重值
    protected Remove m_attributeFilter;
    protected int m_nValid;//有类别信息的数据数
    protected int m_numClasses;
    protected int[] m_reducedIndexSet;//所选特征索引号
    protected String m_selectedResult = "";//选择结果字符串
    protected boolean m_isNumeric;//if class numeric
    /** Treat missing values as seperate values */
    protected boolean m_missingSeperate;
    protected Instances[] m_data;
    protected Instances m_labeledInstances;
    private DataSpliter m_dataSpliter;
    protected int m_nLabeled;//标记样本的个数

    public SemiAttributeSelector() {
    }

    public void buildSelector(Instances data) throws Exception {
        m_trainInstances = data; //训练数据
        m_classIndex = m_trainInstances.classIndex(); //类别索引
        m_numAttribs = m_trainInstances.numAttributes(); //特征数目
        m_numInstances = m_trainInstances.numInstances(); //样本数目
        m_numClasses = m_trainInstances.numClasses();
        m_isNumeric = m_trainInstances.attribute(m_classIndex).isNumeric();
        m_missingSeperate = false;

        initReducedIndexSet();
        m_trainInstances.setClassIndex(m_classIndex);
        m_IsLabeled = new boolean[m_numInstances];

        Instances templeInst = new Instances(m_trainInstances);
        templeInst.delete();
        m_labeledInstances = new Instances(templeInst);
        m_data = new Instances[m_numClasses];
        for (int x = 0; x < m_numClasses; x++) {
            m_data[x] = new Instances(templeInst);
        }
        m_dataSpliter=new DataSpliter(m_trainInstances,m_labeledInstances,m_data,m_nLabeled,m_IsLabeled);
        m_dataSpliter.split();
    }

    ;

    abstract public void startSelector();

    /**
     * Returns the capabilities of this evaluator.
     *
     * @return            the capabilities of this evaluator
     * @see               Capabilities
     */
    public Capabilities getCapabilities() {
        return new Capabilities(this);
    }

    /**
     * Returns the revision string.
     *
     * @return		the revision
     */
    public String getRevision() {
        return RevisionUtils.extract("$Revision: 1.15 $");
    }

    public Instances reduceDimensionality(Instances in) throws Exception {
        if (m_reducedIndexSet != null) {
            m_attributeFilter = new Remove();
            m_attributeFilter.setAttributeIndicesArray(m_reducedIndexSet);
            m_attributeFilter.setInvertSelection(true);
            m_attributeFilter.setInputFormat(in);
        }
        if (m_attributeFilter == null) {
            throw new Exception("No feature selection has been performed yet!");
        }
        return Filter.useFilter(in, m_attributeFilter);
    }

    @Override
    public String toString() {
        StringBuffer text = new StringBuffer();

        if (m_trainInstances == null) {
            text.append("Selector has not been built yet\n");
        } else {
            text.append("\tWeight of labeled distance: " + m_labeleWeight + "\n");
            text.append("\tWeight of unlabeled distance: " + m_unlabeleWeight + "\n");
            text.append("\tSelected Attribute: " + m_selectedResult + "\n");
        }
        return text.toString();
    }

    private String getOptionsString(String[] options) {
        String str = "";
        for (int i = 0; i < options.length; i++) {
            str += options[i] + " ";
        }
        return str;
    }

    public void runSemiAttributeSelector(String[] options) {
        String trainFileName, savePath;
        Instances train = null;
        String[] optionsTmp = (String[]) options.clone();
        boolean helpRequested = false;


        // get basic options (options the same for all attribute selectors
        try {
            trainFileName = Utils.getOption('i', options);
            savePath = Utils.getOption('p', options);
            helpRequested = Utils.getFlag('h', options);
            if (helpRequested || (trainFileName.length() == 0)) {

                if (helpRequested) {
                    throw new Exception("Help requested.");
                } else {
                    throw new Exception("No training file given.");
                }
            }

            DataSource source = new DataSource(trainFileName);
            train = source.getDataSet();
            train.setClassIndex(train.numAttributes() - 1);
            this.setOptions(options);
            this.buildSelector(train);
            this.startSelector();
            Instances reducedInst = this.reduceDimensionality(train);
            StringBuffer text = new StringBuffer();

            int ind1 = trainFileName.lastIndexOf('\\');
            int ind2 = trainFileName.lastIndexOf('.');

            String fileName = trainFileName.substring(ind1 + 1);
            String paraString = getOptionsString(getOptions());

            text.append("\nTrainInstances:" + fileName + "\n");
            text.append("Options:" + paraString + "\n");
            text.append("Selected Attribute: " + m_selectedResult + "\n");

            
            String name = trainFileName.substring(ind1 + 1, ind2);
            String pathString = savePath + "\\" + name + "\\";

            String saveDataFileName = pathString.concat(name).concat("(").concat(paraString).concat(")").concat(".arff");
            String saveTextFileName = pathString.concat(name).concat(".txt");

            MySave.SaveInstances(reducedInst, saveDataFileName);
            MySave.SaveStringBuffer(text.toString(), saveTextFileName);
        } catch (Exception e) {
            System.err.println("Error:"+e.getMessage());
        }

    }

    abstract protected void initReducedIndexSet();

    abstract public void setOptions(String[] options) throws Exception;

    abstract public String[] getOptions();
}
